﻿using System;
using System.Collections.Generic;

namespace GameFramework.ObjectPool
{
    public class ObjectLRUCache<T>
    {
        public class Node<V>
        {
            public V val;
            public Node<V> pre;
            public Node<V> next;
            public Node(V val = default(V))
            {
                this.val = val;
            }
        }

        public Dictionary<T, Node<T>> dict = new Dictionary<T, Node<T>>();

        private Stack<Node<T>> nodePool = new Stack<Node<T>>();

        private Node<T> head;
        private Node<T> tail;

        public ObjectLRUCache()
        {
            head = new Node<T>();
            tail = new Node<T>();
            head.next = tail;
            tail.pre = head;
        }

        //数量

        public int Count
        {
            get
            {
                return dict.Count;
            }
        }

        //是否存在
        public bool Contains(T val)
        {
            return dict.ContainsKey(val);
        }

        //返回最后的元素
        public T Peek()
        {
            if (Count == 0) return default(T);
            return tail.pre.val;
        }

        //删除最后的元素
        public T Pop()
        {
            if (Count == 0) return default(T);
            T val = tail.pre.val;
            Remove(val);
            return val;
        }

        //添加元素
        public void Add(T val)
        {
            if(Contains(val))
            {
                //更新val的位置
                var node = dict[val];
                RemoveNode(node);
                MarkNew(node);
            }
            else
            {
                var node = CreateNode(val);
                MarkNew(node);
            }
        }

        //移除元素
        public void Remove(T val)
        {
            if (!Contains(val)) return;
            var node = dict[val];
            RemoveNode(node);
            dict.Remove(node.val);
            RecycleNode(node);
        }

        //清空缓存
        public void Clear()
        {
            foreach (var node in dict.Values) RecycleNode(node);
            dict.Clear();
        }

        private void RemoveNode(Node<T> node)
        {
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }

        private void MarkNew(Node<T> node)
        {
            dict[node.val] = node;
            head.next.pre = node;
            node.pre = head;
            node.next = head.next;
            head.next = node;
        }

        private Node<T> CreateNode(T val)
        {
            if(nodePool.Count > 0)
            {
                var node = nodePool.Pop();
                node.val = val;
                return node;
            }
            Node<T> newNode = new Node<T>(val);
            return newNode;
        }

        private void RecycleNode(Node<T> node)
        {
            node.pre = node.next = null;
            node.val = default(T);
            nodePool.Push(node);
        }
    }
}
